package com.component;

import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.filter.AssignableTypeFilter;

import java.io.IOException;
import java.util.Set;

/**
 * @author fanxiao  2022/3/29
 * @since 1.0.0
 */
public class CandidatesClassPathScanner extends ClassPathBeanDefinitionScanner {

	private Class<?> markerInterface;

	private Class<? extends CandidatesFactoryBean> factoryBeanClass;


	public void setMarkerInterface(Class<?> markerInterface) {
		this.markerInterface = markerInterface;
	}

	public CandidatesClassPathScanner(BeanDefinitionRegistry registry) {
		super(registry, false);
	}

	public void setFactoryBeanClass(Class<? extends CandidatesFactoryBean> factoryBeanClass) {
		this.factoryBeanClass = factoryBeanClass;
	}

	public void registerFilter() {
		boolean acceptAllInterfaces = true;

		if (this.markerInterface != null){
			addIncludeFilter(new AssignableTypeFilter(this.markerInterface) {
				@Override
				protected boolean matchClassName(String className) {
					return false;
				}
			});
			acceptAllInterfaces = false;
		}

		if (acceptAllInterfaces) {
			// default include filter that accepts all classes
			addIncludeFilter((metadataReader, metadataReaderFactory) -> true);
		}
	}


	@Override
	protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
		Set<BeanDefinitionHolder> beanDefinitionHolders = super.doScan(basePackages);
		GenericBeanDefinition beanDefinition;
		if (beanDefinitionHolders != null) {
		  for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
			  //
			  beanDefinition = (GenericBeanDefinition) beanDefinitionHolder.getBeanDefinition();
			  beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(beanDefinition.getBeanClassName());
			  beanDefinition.setBeanClass(this.factoryBeanClass);
		  }
		}

		return beanDefinitionHolders;
	}


	@Override
	protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
		return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
	}

	@Override
	protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException {
		if (super.checkCandidate(beanName, beanDefinition)) {
			return true;
		} else {
			return false;
		}
	}
}